﻿using System;
using System.Reflection;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using TestClasses;
using TestClasses.Fakes;

namespace AutoFakesTest.Spikes
{
    using System.Collections.Generic;
    using System.Linq;

    [TestClass]
    public class ReflectionSpikes
    {
        [TestMethod]
        public void HowToInstantiateAReferenceTypeViaReflection()
        {
            Type t = typeof (ConcreteClassNoDependencyInjection);
            var constructors = t.GetConstructors();
            var o = constructors[0].Invoke(null);
            Assert.IsInstanceOfType(o, typeof (ConcreteClassNoDependencyInjection));
        }

        [TestMethod]
        public void HowToCallAConstructorWithAnInterfaceParameterViaReflection()
        {
            Type t = typeof (ConcreteClassWithOneInterfaceInConstructor);
            var constructors = t.GetConstructors();
            IInterface1 i1 = new StubIInterface1();
            var parms = new object[] {i1};
            var o = constructors[0].Invoke(parms);
            Assert.IsInstanceOfType(o, typeof (ConcreteClassWithOneInterfaceInConstructor));
        }

        [TestMethod]
        public void HowToDynamicallyCallAConstructorForATypeForWhichYouOnlyHaveItsName()
        {
            Assembly[] loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies();
            var assmeblyList = new List<Assembly>(loadedAssemblies);
            assmeblyList.Insert(0, Assembly.GetExecutingAssembly());

            Type[] types = null;
            foreach (var assembly in assmeblyList)
            {
                foreach (var module in assembly.GetModules(false))
                {
                    types = module.FindTypes(Module.FilterTypeName, "StubIInterface1");
                    if (types.Length > 0)
                        goto Found;  // Yes, just used a "goto", think it's legit to break out of two loops
                }
            }

            Found:
            Assert.IsNotNull(types);
            Assert.IsTrue(types.Length > 0);
            var constructors = types[0].GetConstructors();
            var o = constructors[0].Invoke(null);
            Assert.IsInstanceOfType(o, typeof (StubIInterface1));
        }

        [TestMethod]
        public void
            HowToDynamicallyCallAConstructorForATypeForWhichYouOnlyHaveItsNameAndItsAssemblyIsReferencedButNotYetLoaded()
        {
            Assembly[] loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies();
            var assemblyList = new List<Assembly>(loadedAssemblies);
            assemblyList.Insert(0, Assembly.GetExecutingAssembly());

            Type[] types = null;
            foreach (var assembly in assemblyList)
            {
                foreach (var module in assembly.GetModules(false))
                {
                    types = module.FindTypes(Module.FilterTypeName, "StubIInterface1");
                    if (types.Length > 0)
                        goto Found;
                }
            }

            Found:
            if (types == null || types.Length == 0)
            {
                var assemblyNameInfo = Assembly.GetExecutingAssembly().GetReferencedAssemblies();
                foreach (var assemblyName in assemblyNameInfo)
                {
                    bool thisAssemblyIsLoaded = assemblyList.Any(assembly => assemblyName.FullName == assembly.FullName);
                    if (!thisAssemblyIsLoaded)
                    {
                        Assembly newAssembly = Assembly.Load(assemblyName);
                        foreach (var module in newAssembly.GetModules(false))
                        {
                            types = module.FindTypes(Module.FilterTypeName, "StubInterface1");
                            if (types.Length > 0)
                                goto Found2;
                        }
                    }

                }


            }
            Found2:
            Assert.IsNotNull(types);
            Assert.IsTrue(types.Length > 0);
            var constructors = types[0].GetConstructors();
            // ReSharper disable UnusedVariable
            var o = constructors[0].Invoke(null);
            // ReSharper restore UnusedVariable
        }
    }
}